#!/usr/bin/tclsh

## $Id: cpp2dia.tclsh,v 1.7 2003/05/14 09:20:00 codemangler Exp $

#######################################################
## Copyright (c) 2001-2003 Achim Mueller
## 
## This software is provided 'as-is', without any 
## express or implied warranty. In no event will the 
## authors be held liable for any damages arising from 
## the use of this software.
## 
## Permission is granted to anyone to use this software 
## for any purpose, including commercial applications, 
## and to alter it and redistribute it freely, subject 
## to the following restrictions:
## 
## 1. The origin of this software must not be 
## misrepresented; you must not claim that you wrote 
## the original software. If you use this software in 
## a product, an acknowledgment in the product 
## documentation would be appreciated but is not 
## required.
## 
## 2. Altered source versions must be plainly marked as 
## such, and must not be misrepresented as being the 
## original software.
## 
## 3. This notice may not be removed or altered from 
## any source distribution.
## 
#######################################################

#######################################################
##
## Config Section 
## is after the definition of "updateRCfile"
## please scroll down
##
#######################################################

global aConfig
global lConfigKeys
set lConfigKeys { install_path
	dot_layouter 
	dot_graphtype             glob_header 
	glob_source               output_filename 
	ctags_cmd                 graph_generalizations 
	graph_associations        syntax_ignoreVoidResult 
	syntax_ignoreVoidArgument syntax_ignoreInline 
	syntax_ignoreStatic       syntax_ignoreVirtual 
	syntax_prefixVirtual 
}
global aHelp
array set aHelp {
	install_path {this is the path where cpp2dia and its files are located
			e.g. "/usr/local/cpp2dia"}
	dot_layouter {this is the command to layout the graph with graphviz
			e.g. "/usr/local/neato"}
	dot_graphtype {should the dot_layouter use a directed or undirected
			graph (graph/digraph)}
	glob_header {globbing for headerfiles
			e.g. "*.h *.hxx"}
	glob_source {globbing for sourcefiles
			e.g. "*.cxx *.cpp"}
	output_filename {set the name of the outputfile
			e.g. "output.dia"}
	ctags_cmd {this is the command to create a ctags file
			e.g. "/usr/bin/ctags -n --fields=+aim"}
	graph_generalizations {this is used to switch generalization edges in the
			UML graph on/off (1/0)}
	graph_associations {this is used to switch association edges in the
			UML graph on/off (1/0)}
	syntax_ignoreVoidResult {this is used to toggle display of "void" return 
			datatype on/off (1/0)}
	syntax_ignoreVoidArgument {this is used to toggle display of "void" argument 
			datatype on/off (1/0)}
	syntax_ignoreInline {this is used to toggle display of "inline" modifier
			on/off (1/0)}
	syntax_ignoreStatic {this is used to toggle display of "static" modifier
			on/off (1/0)}
	syntax_ignoreVirtual {this is used to toggle display of "virtual" modifier
			on/off (1/0)}
	syntax_prefixVirtual {this is used to toggle add of "v:" prefix to function 
			member if virtual on/off (1/0)}
}

#######################################################
##
## updateRCfile
##
#######################################################
proc updateRCfile {configfile import} {
	global aConfig
	global lConfigKeys	
	global aHelp
	global env
	set iConfigVersion 1
		
	set aDefault(install_path) [file join $env(HOME) "cpp2dia" ]
	set aDefault(dot_layouter) [file join $env(HOME) "gv1.7c" bin neato ]
	set aDefault(dot_graphtype) "graph"
	set aDefault(glob_header) "*.h *.hxx"
	set aDefault(glob_source) "*.cxx *.cpp"
	set aDefault(output_filename) "output.dia"
	set aDefault(ctags_cmd) "[file join "/" usr bin ctags] -n --fields=+aim --c++-types=+cegmpstuvx-dnf --if0=yes"
	set aDefault(graph_generalizations) 1
	set aDefault(graph_associations) 1
	set aDefault(syntax_ignoreVoidResult) 1
	set aDefault(syntax_ignoreVoidArgument) 1
	set aDefault(syntax_ignoreInline) 1
	set aDefault(syntax_ignoreStatic) 1
	set aDefault(syntax_ignoreVirtual) 1
	set aDefault(syntax_prefixVirtual) 1
	
	if {$import} {
		set bGotVersion 0
		set fi [open $configfile "r"]
		while {![eof $fi]} {
			set line [string trim [gets $fi]]
			if {$line == ""} {
				continue
			}
			if {[string index $line 0] == "#"} {
				continue
			}
			set entry [lindex $line 0]
			if {([lsearch $lConfigKeys $entry ] >= 0) || ($entry == "config_version")} {
				if {($bGotVersion == 0) && ($entry != "config_version")} {
					puts "ERROR: reading $configfile: 'config_version' must be first entry!"
					exit 0
				}
				if { [catch {set aConfig($entry) [lindex $line 1]}]} {
					puts "ERROR: reading $configfile: broken data in entry '$entry'"
					exit 0
				} else {
					set bGotVersion 1
				}
			} else {
				puts "ERROR: reading $configfile: unknown entry '$entry'"
				exit 0
			}
		}
		close $fi
	}
	
	set needStore 0
	foreach entry $lConfigKeys {
		if {![info exists aConfig($entry)]} {
			set aConfig($entry) $aDefault($entry)
			set needStore 1
		}
	}	
	
	if {[info exists aConfig(config_version)]} {
		if {$aConfig(config_version) < $iConfigVersion } {
			set needStore 1
		}
	} else {
		set needStore 1
	}
	
	if {$needStore} {
		set fo [open $configfile "w"]
		puts $fo "config_version $iConfigVersion\n"
		foreach entry $lConfigKeys {
			puts $fo "##"
			foreach l [split $aHelp($entry) "\n"] {
				puts $fo "## [string trim $l]"
			}
			puts $fo "##"
			puts $fo "$entry [list $aConfig($entry)]"
			puts $fo ""
		}
		close $fo
	}
}

#######################################################
##
## Config Section
##
#######################################################

set aConfig(config_file) [file join $env(HOME) ".cpp2diarc"]

## the config section has changed. If you run cpp2dia
## for the first time it creates a $HOME/.cpp2diarc file
## that contains the known config section.

#######################################################
##
## End of Config Section
##
#######################################################

if {![file exists $aConfig(config_file)]} {
	updateRCfile $aConfig(config_file) 0	
	puts "a new configfile '$aConfig(config_file)' has been created."
	puts "please edit it an rerun 'cpp2dia.tclsh'."
	exit 0
} else {
	updateRCfile $aConfig(config_file) 1
}

source [file join $::aConfig(install_path) cpp2dia_procs.tcl]
source [file join $::aConfig(install_path) cpp2dia_output.tcl]
source [file join $::aConfig(install_path) cpp_tokenize.tcl]

set sVersion "1.2.0"

#############################
## handle command line args #
#############################

set bShowHelp 0
foreach {arg val} $argv {
	if {[string range $arg 0 1] != "--"} {
		puts "ERROR: '$arg' must start with '--'"
		set bShowHelp 1
	} else {
		if {[string tolower $arg] == "help" } {
			set bShowHelp 1
		} elseif {[lsearch $lConfigKeys $arg] >= 0} {
			set aConfig($arg) $val
		} else {
			puts "ERROR: unknown argument '$arg' "
			set bShowHelp 1
		}
	}
}

if {$bShowHelp} {
	puts "cpp2dia Version: $sVersion (c) 2001-2003 by Achim Mueller"
	set res ""
	foreach entry $lConfigKeys {
		append res  "--${entry}"
		foreach l [split $aHelp($entry) "\n"] {
			append res "\n\t\t[string trim $l]"
		}
		append res " (set to [list $aConfig($entry)])\n"
	}
	puts $res
	exit 0
}

array set aDiaTree {}

set xml_diagram [CreateXMLObj aDiaTree "dia:diagram" [list "xmlns:dia" "http://www.lysator.liu.se/~alla/dia/"] ]
set xml_diagramdata [CreateDIADiagramdata aDiaTree]
AddXMLChildObj aDiaTree $xml_diagram $xml_diagramdata	
set xml_layer [CreateXMLObj aDiaTree "dia:layer" [list "name" "Background" "visible" "true"] ]
AddXMLChildObj aDiaTree $xml_diagram $xml_layer	

set USE_LAYOUTER 0

if {[info exists aConfig(dot_layouter)]} {
	if {$aConfig(dot_layouter) != ""} {
		set USE_LAYOUTER 1
	}
}

set lallHeaderFiles [list]
foreach g $::aConfig(glob_header) {
	if {![catch {set lTemp [glob $g]}]} {
		set lallHeaderFiles [concat $lallHeaderFiles $lTemp]
	}
}

set lallSourceFiles [list]
foreach g $::aConfig(glob_source) {
	if {![catch {set lTemp [glob $g]}]} {
		set lallSourceFiles [concat $lallSourceFiles $lTemp]
	}
}

if {[llength $lallHeaderFiles] == 0 && [llength $lallSourceFiles] == 0} {
	puts "ERROR: No files found! Nothing to do."
	exit 0
}


set filecounter 1
set counter 1

array set aClasses {}
array set aMethods {}
array set aAttribs {}
array set aAssociations {}

array set aClassData {}

array set aSourceTags {}
array set aFiles {}
array set aCustomDataTypes {}
foreach t $::ACM::CPP::lCppDataTypes {
	set aCustomDataTypes($t) 1
}
foreach t $::ACM::CPP::lCppTypeMods {
	set aCustomDataTypes($t) 1
}

########################################
## execute ctags
########################################
if {$::aConfig(ctags_cmd) != ""} {
	eval "exec $::aConfig(ctags_cmd) $lallHeaderFiles $lallSourceFiles"
}

########################################
## read 'tags' files generated by ctags
########################################
if {[catch {set fi [open "tags" "r"]}] } {
	puts "ERROR: opening 'tags' file"
	exit 0
}
array set aMissing {}
array set aFiles {}
while {![eof $fi]} {
	set line [gets $fi]
	if {($line == "") || ([string index $line 0] == "!")} {
		continue
	}

	set i [string first "\t" $line]
	set text [string range $line 0 [expr $i-1]]
	set line [string range $line [expr $i+1] end]
	
	set i [string first "\t" $line]
	set file [string range $line 0 [expr $i-1]]
	set line [string range $line [expr $i+1] end]

	set i [string last ";\"" $line]
	set pattern [string range $line 0 [expr $i-1]]
	set line [string range $line [expr $i+3] end]
	
	if {[string index $pattern 0] == "/"} {
		set pattern [string range $pattern 2 [expr [string length $pattern]-3]]
		set idx 0
		set temp ""
		while {$idx < [string length $pattern]} {
			set c [string index $pattern $idx]
			if {$c == "\\"} {
				incr idx
				set c [string index $pattern $idx]
			}
			append temp $c
			incr idx
		}
		set pattern $temp
		set linenumber -1
		set aMissing($file,$pattern) $text
	} else {
		set linenumber $pattern
		set pattern ""
	}
	set type [string index $line 0]
	set line [string range $line 2 end]


	
	if {[lsearch [array names aFiles] $file] == -1} {
		set aFiles($file) $filecounter
		set fileindex $filecounter
		incr filecounter
	} else {
		set fileindex $aFiles($file)
	}
	
	switch -exact -- $type {
		"s" -
		"u" -
		"t" -
		"g" { 
				## s = struct
				## u = union
				## t = typedef
				## g = enumname (not its elements)
				## e = enum element names ( not considered )
				## d = define ( not considered )
				set aCustomDataTypes($text) "1"
			}

		"c" { 
				##lappend aClasses($text) [list $file $fileindex $pattern $linenumber $type $line]	
				lappend aSourceTags($file,$linenumber) [list $linenumber $type $text "" ]
				lappend aClasses($text) 1
				set aCustomDataTypes($text) "1"
				set inherits ""
				foreach sItem $line {
					set lItem [split $sItem ":"]
					set nam [lindex $lItem 0]
					set $nam [lindex $lItem 1]
				}	
				incr counter
				set aClassData($text,id) "O$counter"
				set aClassData($text,name) "$text"
				set aClassData($text,inherits) [split $inherits ","]
				set aClassData($text,stereotype) ""
				set aClassData($text,istemplate) 0
				set aClassData($text,templateargs) ""
				set aClassData($text,abstract) false
			}

		"m" { 
				##lappend aAttribs($text) [list $file $fileindex $pattern $linenumber $type $line]
				set class ""
				set access "public"
				foreach sItem $line {
					set lItem [split $sItem ":"]
					set nam [lindex $lItem 0]
					set $nam [lindex $lItem 1]
				}	
				if {$class != ""} {
					incr counter
					set pattern [string trim $pattern]
					set datatype [string range $pattern 0 [expr [string last $text $pattern]-1]]
					set datatype [string trim $datatype]
					regsub -all { \*} $datatype {*} datatype
					regsub -all { \&} $datatype {&} datatype
					set aClassData($class,attrib,$access,$text) "$class,$text"
					set aAttribs($class,$text,class) $class
					set aAttribs($class,$text,name) $text
					set aAttribs($class,$text,access) $access
					set aAttribs($class,$text,type) $datatype
					set aAttribs($class,$text,prototype) ""
					lappend aSourceTags($file,$linenumber) [list $linenumber $type $text "$class,$text" ]

				}
			}
		"p" { 
				regsub -all { } $text {} text
				##lappend aAttribs($text) [list $file $fileindex $pattern $linenumber $type $line]
				set class ""
				set access "public"
				foreach sItem $line {
					set lItem [split $sItem ":"]
					set nam [lindex $lItem 0]
					set $nam [lindex $lItem 1]
				}	
				if {$class != ""} {
					incr counter
					set pattern [string trim $pattern]
					set datatype [string range $pattern 0 [expr [string last $text $pattern]-1]]
					set datatype [string trim $datatype]
					regsub -all { \*} $datatype {*} datatype
					regsub -all { \&} $datatype {&} datatype

					set aClassData($class,method,$access,$counter) "$class,$counter"
					set aMethods($class,$counter,class) $class
					set aMethods($class,$counter,access) $access
					set aMethods($class,$counter,name) $text
					set aMethods($class,$counter,type) $datatype
					set aMethods($class,$counter,args) ""
					set aMethods($class,$counter,prototype) ""
					set aMethods($class,$counter,virtual) 0
					set isPureVirtualFunction 0
					if {$isPureVirtualFunction} {
						set aMethods($class,$counter,abstract) "true"
					} else {
						set aMethods($class,$counter,abstract) "false"
					}

					lappend aSourceTags($file,$linenumber) [list $linenumber $type $text "$class,$counter" ]
				}
			}
	}
}
close $fi

############################################
##  now we have to parse all header files anyway to get all function members
############################################
foreach sFile $lallHeaderFiles {
	set fi [open $sFile "r"]
	set sClass ""
	set sAccess "public"
	set lTags [array names aSourceTags "$sFile,*"]
	set lLinesNums [list]
	foreach t $lTags {
		foreach it $aSourceTags($t) {
			lappend lLinesNums [lindex $it 0]
		}
	}
	set lLinesNums [lsort -integer $lLinesNums]
	set iLineNum 0	
	set iLineIdx 0
	set iNextLine [lindex $lLinesNums $iLineIdx]
	set iBraceLevel 0
	set iParenLevel 0
	set iInComment 0
	set sDataType ""
	set bInClass 0
	array set aItems {}
	set sClass ""
	set sFunctionID ""
	set bIsTemplate 0
	set sTemplateArgs ""
	while {![eof $fi]} {
		set line [gets $fi]
		incr iLineNum
		if {[string trim $line] == ""} {
			continue
		}
		if {$iLineNum == $iNextLine } {
			set lItems $aSourceTags($sFile,$iLineNum)
			unset aItems
			array set aItems {}
			foreach it $lItems {
				set aItems([lindex $it 2]) [list [lindex $it 1] [lindex $it 3]]
			}
			incr iLineIdx
			set iNextLine [lindex $lLinesNums $iLineIdx]
			set bTagLine 1
		} else {
			set bTagLine 0
		}
		set iColum 0
		set sTType ""
		while {[set sToken [::ACM::CPP::getNextToken line iColum sTType]] != ""} {
		
			if {$sToken == "//"} {
				set line ""
				set iColum 5
				continue
			} elseif {$sToken == "/**"} {
				set iInComment 1
				continue
			} elseif {$sToken == "/*"} {
				set iInComment 1
				continue
			} elseif {$sToken == "*/"} {
				set iInComment 0
				continue
			}
			
			if {$iInComment} {
				continue
				
			} elseif {$sToken == "\{"} {
				incr iBraceLevel
				set sDataType ""
				set sFunctionID ""
				continue
				
			} elseif {$sToken == "\}"} {
				incr iBraceLevel -1
				set sDataType ""
				set sFunctionID ""
				set bIsTemplate 0
				set sTemplateArgs ""
				if { $iBraceLevel == 0} {
					set bInClass 0
				}
				continue
				
			} elseif {$sToken == "("} {
				incr iParenLevel
				set sDataType ""
				continue
				
			} elseif {$sToken == ")"} {
				incr iParenLevel -1
				set sDataType ""
				if { $iParenLevel == 0} {
					set sFunctionID ""
				}
				continue
				
			} elseif {$sToken == ";"} {
				set sDataType ""
				set bSearchInterface 0	
				set sFunctionID ""
				set bIsTemplate 0
				set sTemplateArgs ""
				continue
				
			} elseif { ($sFunctionID != "") && ($iParenLevel > 0) } {
				append aMethods($sFunctionID,args) $sToken
				continue
			} elseif { $bIsTemplate == 1 } {
				append sTemplateArgs $sToken
			}

			

			if {$sToken == "template"} {
				set bIsTemplate 1
			
			} elseif {$sToken == "operator"} {
				set dummyidx $iColum
				set dummytype ""
				set dummytoken [::ACM::CPP::getNextToken line dummyidx dummytype]				
				append sToken $dummytoken
				set sTType $dummytype
				set iColum  $dummyidx
			}

			if {$bTagLine } {
				if {[info exists aItems($sToken)]} {
					switch -exact -- [lindex $aItems($sToken) 0] {
						"m" {
								if {$::aConfig(syntax_ignoreStatic)} {
									regsub {void} $sDataType {} sDataType
								}
								regsub -all {\t} $sDataType { } sDataType
								regsub -all {  } $sDataType { } sDataType
								regsub { <} $sDataType {<} sDataType
								regsub {< } $sDataType {<} sDataType
								regsub { >} $sDataType {>} sDataType
								regsub {> } $sDataType {>} sDataType
								regsub { \*} $sDataType {\*} sDataType
								regsub { \&} $sDataType {\&} sDataType
								set aAttribs($sClass,$sToken,type) [string trim $sDataType]

								set sDataType ""
							}
						"c" {
								if {$bInClass == 0} {
									set sDataType ""
									set sClass $sToken
									set bInClass 1
									if {$bIsTemplate} {
										set aClassData($sClass,istemplate) 1
										set i [string last ">" $sTemplateArgs]
										if {$i == -1} {
											set i [string last "class" $sTemplateArgs]
										}
										set sTemplateArgs [string range $sTemplateArgs 0 [expr $i-1]]
										set aClassData($sClass,templateargs) $sTemplateArgs
										buildTemplateArgList aClassData($sClass,templateargs) aCustomDataTypes
										set sTemplateArgs ""
									}
									set bIsTemplate 0
									puts "parse class '$sToken'..."
								}
							}
						"p" {
								if {[info exists aSourceTags($sFile,$iLineNum)]} {
									foreach it $aSourceTags($sFile,$iLineNum) {
										set temptype [lindex $it 1]
										set temppattern [lindex $it 2]
										set tempid [lindex $it 3]
										if {($temptype == "p") && ($temppattern == $sToken)} {
											if {[string first "virtual" $sDataType] >= 0} {
												set aMethods($tempid,virtual) 1
												if {$aConfig(syntax_ignoreVirtual)} {
													regsub {virtual} $sDataType {} sDataType
												}
											}
											if {$aConfig(syntax_ignoreInline)} {
												regsub {inline} $sDataType {} sDataType
											}
											if {$aConfig(syntax_ignoreVoidResult)} {
												regsub {void} $sDataType {} sDataType
											}
											regsub -all {\t} $sDataType { } sDataType
											regsub -all {  } $sDataType { } sDataType
											regsub { <} $sDataType {<} sDataType
											regsub {< } $sDataType {<} sDataType
											regsub { >} $sDataType {>} sDataType
											regsub {> } $sDataType {>} sDataType
											regsub { \*} $sDataType {\*} sDataType
											regsub { \&} $sDataType {\&} sDataType
											set aMethods($tempid,type) [string trim $sDataType]
											set sFunctionID $tempid
										}
									}
								}
								set sDataType ""
							}
					}
				} else {
					if {$sToken == ":"} {
						set sDataType ""
					} else {
						append sDataType $sToken
					}
				}

			} else {
				if {$sToken == ":"} {
					set sDataType ""
				} else {
					append sDataType $sToken
				}
			}
		}
	}
}
foreach args [array names aMethods "*,args"] {
	buildArgumentList aMethods($args) aCustomDataTypes
	set id [string range $args 0 [expr [string length $args]-6]]
	set aMethods($id,prototype) [buildMethodPrototype "$id" aMethods]	
}
foreach args [array names aAttribs "*,prototype"] {
	set id [string range $args 0 [expr [string length $args]-11]]
	set aAttribs($id,prototype) [buildAttribPrototype "$id" aAttribs]	
	##set aAttribs($class,$text,prototype) [buildAttribPrototype "$class,$text" aAttribs]
}

set lAllClasses [lsort -dictionary [array names aClasses]]


#############################################
##
## calc all associations
##
#############################################
puts "calculate associations..."
foreach sClass $lAllClasses {
	set diagramhandle 8
	foreach {access sVisibility} {private 1 protected 2 public 0} {		
		foreach sAttr [lsort -dictionary [array names aClassData "$sClass,attrib,$access,*"]] {
			set id $aClassData($sAttr)
			set datatype $aAttribs($id,type)
			regsub -all {[\&\*]} $datatype {} datatype
			if {[set i [string last "<" $datatype]] >=0} {
				set j [string first ">" $datatype]
				set datatype [string trim [string range $datatype [expr $i+1] [expr $j-1]]]
			}
			set datatype [lindex $datatype end]
			if {([lsearch -exact $lAllClasses $datatype] >= 0) && ($sClass != $datatype)} {
				incr counter		
				set aAssociations($counter,id) $counter
				set aAssociations($counter,to) $sClass
				set aAssociations($counter,from) $datatype
				set aAssociations($counter,access) $access
				set aAssociations($counter,handle) $diagramhandle
			}
			incr diagramhandle 2
		}
	}
}


foreach sClass $lAllClasses {
	dimensionClass $sClass aClassData aMethods aAttribs
}

set x 1.0
set y 1.0
set maxy 2.0

foreach sClass $lAllClasses {
	set aClassData($sClass,pos,x) $x
	set aClassData($sClass,pos,y) $y
	set width $aClassData($sClass,width)
	set height $aClassData($sClass,height)
	set x [expr $x + $width + 1.0]
	if {$height > $maxy} {
		set maxy $height
	}
	if {$x > 150} {
		set x 1.0
		set y [expr $y + $maxy + 3.0]
		set maxy 2.0
	}
}

if {$USE_LAYOUTER} {
puts "layout diagram..."
	if {$::aConfig(dot_graphtype) == "digraph"} {
		set dotviz "digraph G \{\n"
		set sEdge " -> "
	} else {
		set dotviz "graph G \{\n"
		set sEdge " -- "
	}
	if {$aConfig(graph_generalizations)} {
		foreach sClass $lAllClasses {
			if {[llength $aClassData($sClass,inherits)] > 0} {
				foreach sSuperClass $aClassData($sClass,inherits) {
					append dotviz "\t" $sSuperClass $sEdge $sClass ";\n"
				}
			}
		}
		
	}
	if {$aConfig(graph_associations)} {
		foreach sAttr [array names aAssociations "*,id"] {
			set id $aAssociations($sAttr)
			set datatype $aAssociations($id,from)
			set sClass $aAssociations($id,to)
			append dotviz "\t" $datatype $sEdge $sClass " \[style=dotted\];\n"
		}
	}
	append dotviz "\}\n"
	set fo [open "dot.dot" "w"]
	puts $fo $dotviz
	close $fo
	exec $::aConfig(dot_layouter) dot.dot -o dotout.dot
	readDotFile "dotout.dot" $lAllClasses aClassData 
}


#############################################
##
## add all classes to the XML tree
##
#############################################
puts "create XML tree..."
foreach sClass $lAllClasses {
	set child [CreateDIAClass aDiaTree $sClass aClassData ]
	if {$child != ""} {
		AddXMLChildObj aDiaTree $xml_layer $child
	}
}

#############################################
##
## add all generalizations to the XML tree
##
#############################################
if {$aConfig(graph_generalizations)} {
	foreach sClass $lAllClasses {
		if {[llength $aClassData($sClass,inherits)] > 0} {
			foreach sSuperClass $aClassData($sClass,inherits) {
				incr counter
				set child [CreateDIAGeneralization aDiaTree $sClass aClassData $sSuperClass $counter]
				if {$child != ""} {
					AddXMLChildObj aDiaTree $xml_layer $child
				}
			}
		}
	}
}

#############################################
##
## add all associations to the XML tree
##
#############################################
if {$aConfig(graph_associations)} {
	foreach sAttr [array names aAssociations "*,id"] {
		##puts "\t$sAttr"
		set id $aAssociations($sAttr)
		set child [CreateDIAAssociation aDiaTree $id aClassData aAssociations]
		if {$child != ""} {
			AddXMLChildObj aDiaTree $xml_layer $child
		}
	}
}

set fo [open $aConfig(output_filename) "w"]
puts $fo [WriteXML aDiaTree]
close $fo

return ""
